package ccm.component.oscilloscope;

import ccm.common.CCMConfigManager;
import ccm.common.Configurable;
import ccm.common.FreshControlInterface;
import ccm.common.Utils;
import ccm.common.dragMouse.DragMouse;
import ccm.common.dragMouse.DragMouseListener;
import ccm.common.dragMouse.MouseData;
import ccm.component.override.JButton;
import ccm.component.override.JCheckBox;
import ccm.component.override.JCheckBoxMenuItem;
import ccm.component.override.JFrame;
import ccm.component.override.JLabel;
import ccm.component.override.JSpinner;
import ccm.component.override.*;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLJPanel;
import com.jogamp.opengl.util.awt.TextRenderer;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static ccm.common.Utils.fontResize;
import static ccm.common.Utils.timeFormatter;
import static com.jogamp.opengl.GL.*;
import static com.jogamp.opengl.GL2ES1.GL_POINT_SMOOTH;
import static com.jogamp.opengl.GL2ES1.GL_POINT_SMOOTH_HINT;

public class OscilloscopeJPanel extends JPanel implements ChangeListener, ComponentListener, ActionListener, Configurable, DragMouseListener, FreshControlInterface
{

    static
    {
        CCMConfigManager.setIntegerIfAbsent("ccm/component/oscilloscope/color/cnt",20);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/0",Color.RED);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/1",Color.YELLOW);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/2",Color.BLUE);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/3",Color.MAGENTA);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/4",Color.ORANGE);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/5",Color.GREEN);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/6",Color.PINK);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/7",Color.LIGHT_GRAY);
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/8",new Color(0XD3,0X61,0X35));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/9",new Color(112,152,95));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/10",new Color(191,180,113));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/11",new Color(0XE6,0Xaa,0X68));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/12",new Color(0X99,0X33,0XFF));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/13",new Color(0X00,0X99,0X99));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/14",new Color(0X99,0X99,0X33));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/15",new Color(114,234,47));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/16",new Color(245,7,115));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/17",new Color(0X00,0X66,0X33));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/18",new Color(0X99,0XCC,0X33));
        CCMConfigManager.setColor("ccm/component/oscilloscope/color/19",new Color(0XCC,0X66,0X99));
        //protected static final (.*?) (.*?)\=(.*?)\;
        //CCMConfigManager.set\ u$1IfAbsent("ccm/component/oscilloscope/$2",$3);
        //protected static final $1 $2=CCMConfigManager.get\ u$1("ccm/component/oscilloscope/$2");
        CCMConfigManager.setColor("ccm/component/oscilloscope/backgroundColor",new Color(0X33,0X33,0X33));
        CCMConfigManager.setColor("ccm/component/oscilloscope/axisColor",new Color(0X99,0X99,0X99));
        CCMConfigManager.setFloatIfAbsent("ccm/component/oscilloscope/mouseWidth",2);
        CCMConfigManager.setColor("ccm/component/oscilloscope/mouseColor",new Color(0XF2,0XF2,0XF2));
        CCMConfigManager.setFloatIfAbsent("ccm/component/oscilloscope/curseWidth",6);
        CCMConfigManager.setColor("ccm/component/oscilloscope/curseColor",new Color(0XFF,0X00,0X00));
        CCMConfigManager.setColor("ccm/component/oscilloscope/mouseBackgroundColor",new Color(0X22,0X22,0X22,226));
        CCMConfigManager.setColor("ccm/component/oscilloscope/fpsColor",new Color(0X00,0X70,0X00));
        CCMConfigManager.setIntegerIfAbsent("ccm/component/oscilloscope/channel",24);
    }

    protected final Color[] colors=new Color[CCMConfigManager.getInteger("ccm/component/oscilloscope/color/cnt")];
    protected final Color backgroundColor=CCMConfigManager.getColor("ccm/component/oscilloscope/backgroundColor");
    protected final float lineWidth=3;
    protected final float axisWidth=1;
    protected final Color axisColor=CCMConfigManager.getColor("ccm/component/oscilloscope/axisColor");
    protected final float mouseWidth=CCMConfigManager.getFloat("ccm/component/oscilloscope/mouseWidth");
    protected final Color mouseColor=CCMConfigManager.getColor("ccm/component/oscilloscope/mouseColor");
    protected final float curseWidth=CCMConfigManager.getFloat("ccm/component/oscilloscope/curseWidth");
    protected final Color curseColor=CCMConfigManager.getColor("ccm/component/oscilloscope/curseColor");
    protected final Color mouseBackgroundColor=CCMConfigManager.getColor("ccm/component/oscilloscope/mouseBackgroundColor");
    protected final Color fpsColor=CCMConfigManager.getColor("ccm/component/oscilloscope/fpsColor");
    protected final Font axisFont=CCMConfigManager.getFont("ccm/general/font");
    protected final Font mouseFont=fontResize(CCMConfigManager.getFont("ccm/general/font"),1.2f);
    protected final Font fpsFont=CCMConfigManager.getFont("ccm/general/font");
    protected final Font mouseNameFont=fontResize(CCMConfigManager.getFont("ccm/general/font"),0.8f);

    {
        for(int i=0;i<colors.length;++i)
        {
            colors[i]=CCMConfigManager.getColor("ccm/component/oscilloscope/color/"+i);
        }
    }

    /**
     * 用于绘制波形的画板
     */
    protected final GLJPanel glJPanel;
    /**
     * Y轴缩放输入框
     */
    protected final JSpinner resizeYJSpinner;
    /**
     * 自动Y轴缩放选择
     */
    protected final JCheckBox resizeYJCheckBox;
    /**
     * Y轴偏移输入框
     */
    protected final JSpinner biasYJSpinner;
    /**
     * 自动Y轴偏移选择
     */
    protected final JCheckBox biasYJCheckBox;
    /**
     * X轴缩放输入框
     */
    protected final JSpinner resizeXJSpinner;
    /**
     * X轴偏移输入框
     */
    protected final JCheckBox biasXJCheckBox;
    /**
     * 自动X轴偏移选择
     */
    protected final JSpinner biasXJSpinner;
    /**
     * 自动Y轴缩放选择
     */
    protected final JCheckBoxMenuItem keepZeroInScreenJCheckBox;
    /**
     * 全选
     */
    protected final JCheckBox selectAllJCheckBox;
    /**
     * 自定义名字
     */
    protected final JCheckBoxMenuItem customNameJCheckBox;
    /**
     * 在通道0执行检测
     */
    protected final JCheckBoxMenuItem checkOnChan0JCheckBox;
    /**
     * 在通道0执行检测
     */
    protected final JCheckBoxMenuItem checkOnTimeJCheckBox;
    /**
     * 在显示线的时候一次跨过多个点
     */
    protected final JCheckBoxMenuItem largeStepJCheckBox;
    /**
     * 在显示遇到不连续的时候断开线
     */
    protected final JCheckBoxMenuItem breakLineJCheckBox;
    /**
     * 数据缩放输入框
     */
    protected final JSpinner[] dataResizeJSpinners;
    /**
     * 数据显示选择
     */
    protected final JCheckBox[] dataValueJCheckBox;
    /**
     * 数据值显示
     */
    protected final JLabel[] dataValueJLabels;
    protected final JPanel configJPanel;
    protected final JPanel[] dataJPanel;
    protected final JLabel curseJLabel;
    protected final DragMouse dragMouse;
    private final int[] curse=new int[2];
    protected boolean needFresh;
    /**
     * 数据管理器
     */
    protected OscilloscopeDataManager allDataManager;
    /**
     * 游标
     */
    private int curseI;
    private boolean oscilloscopeGLJPanelNeedFresh;
    private boolean oscilloscopeGLJPanelNeedFastFresh;

    public OscilloscopeJPanel(OscilloscopeDataManager dataManager)
    {
        this();
        this.allDataManager=dataManager;
    }

    public OscilloscopeJPanel()
    {
        super();
        setMinimumSize(new Dimension(1,1));
        setLayout(new BorderLayout());
        /*
         * 生成左边的配置面面板
         */
        configJPanel=new JPanel();
        configJPanel.setBorder(new TitledBorder("示波器设置"));
        JScrollPane jScrollPane=new JScrollPane(configJPanel);
        jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        add(jScrollPane,BorderLayout.EAST);
        configJPanel.setLayout(new VFlowLayout());
        /*
         * 游标
         */
        curseJLabel=new JLabel();
        configJPanel.add(curseJLabel);
        /*
         * 生成的Y轴缩放面板
         */
        JPanel resizeYJSpinnerJPanel=new JPanel();
        configJPanel.add(resizeYJSpinnerJPanel);
        resizeYJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        resizeYJSpinnerJPanel.add(new JLabel("Y轴缩放"));

        resizeYJSpinner=new JSpinner(new SpinnerNumberModel(1,0.0001,10000,0.1));
        resizeYJSpinnerJPanel.add(resizeYJSpinner);
        resizeYJSpinner.addChangeListener(this);
        Dimension dimension=new Dimension(80,(int)(resizeYJSpinner.getFont().getSize()*1.2+10));
        resizeYJSpinner.setPreferredSize(dimension);

        resizeYJCheckBox=new JCheckBox("自动",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/resizeYJCheckBox",true));
        resizeYJSpinnerJPanel.add(resizeYJCheckBox);
        resizeYJCheckBox.addActionListener(this);

        /*
         * 生成的Y轴偏移面板
         */
        JPanel biasYJSpinnerJPanel=new JPanel();
        configJPanel.add(biasYJSpinnerJPanel);
        biasYJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        biasYJSpinnerJPanel.add(new JLabel("Y轴偏移"));

        biasYJSpinner=new JSpinner(new SpinnerNumberModel(0,Integer.MIN_VALUE,Integer.MAX_VALUE,1));
        biasYJSpinnerJPanel.add(biasYJSpinner);
        biasYJSpinner.addChangeListener(this);
        biasYJSpinner.setPreferredSize(dimension);

        biasYJCheckBox=new JCheckBox("自动",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/biasYJCheckBox",true));
        biasYJSpinnerJPanel.add(biasYJCheckBox);
        biasYJCheckBox.addActionListener(this);

        /*
         * 生成的X轴缩放面板
         */
        JPanel resizeXJSpinnerJPanel=new JPanel();
        configJPanel.add(resizeXJSpinnerJPanel);
        resizeXJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        resizeXJSpinnerJPanel.add(new JLabel("X轴缩放"));

        resizeXJSpinner=new JSpinner(new SpinnerNumberModel(1,0.001,1000,0.01));
        resizeXJSpinnerJPanel.add(resizeXJSpinner);
        resizeXJSpinner.addChangeListener(this);
        resizeXJSpinner.setPreferredSize(dimension);
        resizeXJSpinner.setValue(1.0);
        updateResizeXJSpinner();

        /*
         * 生成的X轴偏移面板
         */
        JPanel biasXJSpinnerJPanel=new JPanel();
        configJPanel.add(biasXJSpinnerJPanel);
        biasXJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        biasXJSpinnerJPanel.add(new JLabel("X轴偏移"));

        biasXJSpinner=new JSpinner(new SpinnerNumberModel(0,0,0,50));
        biasXJSpinnerJPanel.add(biasXJSpinner);
        biasXJSpinner.addChangeListener(this);
        biasXJSpinner.setPreferredSize(dimension);

        biasXJCheckBox=new JCheckBox("刷新",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/biasXJCheckBox",true));
        biasXJSpinnerJPanel.add(biasXJCheckBox);
        biasXJCheckBox.addActionListener(this);
        /*
         * 全部开启自动
         */
        dragMouse=new DDDragMouse(this,resizeYJSpinner,resizeYJCheckBox,biasYJSpinner,biasYJCheckBox,resizeXJSpinner,null,biasXJSpinner,biasXJCheckBox);
        dragMouse.updateAutoBiasAndResize();
        /*
         * 生成绘制波形的画板
         */
        add(glJPanel=new OscilloscopeGLJPanel(),BorderLayout.CENTER);
        /*
         * 生成的全选的面板
         */
        JPanel selectAllJPanel=new JPanel();
        configJPanel.add(selectAllJPanel);
        selectAllJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        selectAllJCheckBox=new JCheckBox("全选",true);
        selectAllJPanel.add(selectAllJCheckBox);
        selectAllJCheckBox.addActionListener(this);
        /*
         * 为各个数据生成格子的缩放面板
         */
        dataValueJLabels=new JLabel[CCMConfigManager.getInteger("ccm/component/oscilloscope/channel")];
        dataResizeJSpinners=new JSpinner[dataValueJLabels.length];
        dataValueJCheckBox=new JCheckBox[dataValueJLabels.length];
        dataJPanel=new JPanel[dataValueJLabels.length];
        for(int i=0;i<dataValueJLabels.length;++i)
        {
            configJPanel.add(dataValueJCheckBox[i]=new JCheckBox("",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/custom/"+i,true)));
            dataJPanel[i]=new JPanel();
            configJPanel.add(dataJPanel[i]);
            dataJPanel[i].setLayout(new GridLayout(1,2));
            dataJPanel[i].add(dataResizeJSpinners[i]=new JSpinner(new SpinnerNumberModel(1,0.0001,1000,0.1)));
            dataJPanel[i].add(dataValueJLabels[i]=new JLabel());
            dataResizeJSpinners[i].addChangeListener(this);
            dataValueJCheckBox[i].addActionListener(this);
            dataValueJCheckBox[i].setForeground(colors[i%colors.length]);
            dataValueJLabels[i].setForeground(colors[i%colors.length]);
            dataJPanel[i].setVisible(false);
        }
        boolean checked=true;
        for(int i=0;i<dataValueJLabels.length;++i)
        {
            checked&=dataValueJCheckBox[i].isSelected();
        }
        selectAllJCheckBox.setSelected(checked);
        {

            final JPopupMenu jPopupMenu=new JPopupMenu();
            keepZeroInScreenJCheckBox=new JCheckBoxMenuItem("保持0在屏幕中",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/keepZeroInScreenJCheckBox",true));
            jPopupMenu.add(keepZeroInScreenJCheckBox);
            keepZeroInScreenJCheckBox.addActionListener(this);
            customNameJCheckBox=new JCheckBoxMenuItem("自定义名字",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/customNameJCheckBox",true));
            jPopupMenu.add(customNameJCheckBox);
            customNameJCheckBox.addActionListener(this);
            checkOnChan0JCheckBox=new JCheckBoxMenuItem("检测通道0",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/checkOnChan0JCheckBox",false));
            jPopupMenu.add(checkOnChan0JCheckBox);
            checkOnChan0JCheckBox.addActionListener(this);
            checkOnTimeJCheckBox=new JCheckBoxMenuItem("检测时间间隔",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/checkOnTimeJCheckBox",false));
            jPopupMenu.add(checkOnTimeJCheckBox);
            checkOnTimeJCheckBox.addActionListener(this);
            largeStepJCheckBox=new JCheckBoxMenuItem("显示时忽略数据",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/largeStepJCheckBox",true));
            jPopupMenu.add(largeStepJCheckBox);
            largeStepJCheckBox.addActionListener(this);
            breakLineJCheckBox=new JCheckBoxMenuItem("遇到不连续数据时断开",CCMConfigManager.setBooleanIfAbsent("ccm/component/oscilloscope/breakLineJCheckBox",true));
            jPopupMenu.add(breakLineJCheckBox);
            breakLineJCheckBox.addActionListener(this);
            MouseAdapter mouseAdapter=new MouseAdapter()
            {
                public void mouseClicked(MouseEvent e)
                {
                    if(e.getButton()==MouseEvent.BUTTON3)
                    {
                        jPopupMenu.show((Component)e.getSource(),e.getX(),e.getY());
                    }
                }
            };
            configJPanel.addMouseListener(mouseAdapter);
        }
    }

    /**
     * 更新X轴缩放的范围
     */
    protected void updateResizeXJSpinner()
    {
        if(resizeXJSpinner==null||glJPanel==null)
        {
            return;
        }
        SpinnerNumberModel spinnerNumberModel=((SpinnerNumberModel)resizeXJSpinner.getModel());
        spinnerNumberModel.setValue(Math.max(((Number)spinnerNumberModel.getMinimum()).floatValue(),Math.min(((Number)spinnerNumberModel.getMaximum()).floatValue(),((Number)(spinnerNumberModel.getValue())).floatValue())));
    }

    public void setDataManager(OscilloscopeDataManager dataManager)
    {
        this.allDataManager=dataManager;
    }

    /**
     * 更新游标
     *
     * @return 游标字符串
     */
    protected String updateCurse()
    {
        StringBuilder stb=new StringBuilder();
        if(curseI!=0)
        {
            final int dataLength=allDataManager.getDataLength();
            if(curseI>=1)
            {
                curse[0]=Math.max(0,curse[0]);
            }
            if(curseI>=2)
            {
                curse[1]=Math.max(0,curse[1]);
            }
            if(curseI>=1)
            {
                if(curse[0]>=dataLength)
                {
                    curseI=0;
                }
            }
            if(curseI>=2)
            {
                if(curse[1]>=dataLength)
                {
                    curseI=0;
                }
            }
            if(curseI>=2&&curse[0]>curse[1])
            {
                int tmp=curse[0];
                curse[0]=curse[1];
                curse[1]=tmp;
            }
            if(curseI>=1)
            {
                stb.append("从").append(curse[0]);
            }
            if(curseI>=2)
            {
                stb.append("到").append(curse[1]);
            }
            if(curseI>=2)
            {
                stb.append("共").append(curse[1]-curse[0]).append("条").append(String.format("%.2f",(allDataManager.getTimeUs(curse[1])-allDataManager.getTimeUs(curse[0]))*1e-6)).append("秒");
            }
        }
        if(curseI==0)
        {
            stb.append("按Q放游标，R跟踪游标");
        }
        curseJLabel.setText(stb.toString());
        return stb.toString();
    }

    /**
     * 更新X轴偏移的范围
     */
    protected void updateBiasXJSpinner()
    {
        updateBiasXJSpinner(allDataManager);
    }

    /**
     * 更新X轴偏移的范围
     *
     * @param dataManager 数据管理器
     */
    protected void updateBiasXJSpinner(OscilloscopeDataManager dataManager)
    {
        if(biasXJCheckBox==null||biasXJSpinner==null||dataManager==null)
        {
            return;
        }
        SpinnerNumberModel spinnerNumberModel=((SpinnerNumberModel)biasXJSpinner.getModel());
        int value=(int)biasXJSpinner.getValue();
        int valueMax=dataManager.getDataLength();
        if(biasXJCheckBox.isSelected())
        {
            value=valueMax;
        }
        spinnerNumberModel.setValue(Math.max(0,Math.min(valueMax,value)));
        spinnerNumberModel.setMaximum(valueMax);
    }

    @Override
    public JPanel getConfigJPanel()
    {
        return configJPanel;
    }

    /**
     * 窗体大小变化时更新X轴缩放并重绘
     */
    public void componentResized(ComponentEvent e)
    {
        needFresh=true;
        updateResizeXJSpinner();
    }

    public void componentMoved(ComponentEvent e)
    {

    }

    public void componentShown(ComponentEvent e)
    {

    }

    public void componentHidden(ComponentEvent e)
    {

    }

    /**
     * 输入框数据变化时重绘
     */
    public void stateChanged(ChangeEvent e)
    {
        needFresh=true;
    }

    /**
     * 选择变化时重新绘制
     */
    public void actionPerformed(ActionEvent e)
    {
        if(e.getSource().equals(resizeYJCheckBox))
        {
            dragMouse.enableAutoResizeY(resizeYJCheckBox.isSelected());
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/resizeYJCheckBox",resizeYJCheckBox.isSelected());
        }
        else if(e.getSource().equals(biasYJCheckBox))
        {
            dragMouse.enableAutoBiasY(biasYJCheckBox.isSelected());
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/biasYJCheckBox",biasYJCheckBox.isSelected());
        }
        else if(e.getSource().equals(biasXJCheckBox))
        {
            dragMouse.enableAutoBiasX(biasXJCheckBox.isSelected());
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/biasXJCheckBox",biasXJCheckBox.isSelected());
        }
        else if(e.getSource().equals(customNameJCheckBox))
        {
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/customNameJCheckBox",customNameJCheckBox.isSelected());
        }
        else if(e.getSource().equals(largeStepJCheckBox))
        {
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/largeStepJCheckBox",largeStepJCheckBox.isSelected());
        }
        else if(e.getSource().equals(breakLineJCheckBox))
        {
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/breakLineJCheckBox",breakLineJCheckBox.isSelected());
        }
        else if(e.getSource().equals(checkOnChan0JCheckBox))
        {
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/checkOnChan0JCheckBox",checkOnChan0JCheckBox.isSelected());
        }
        else if(e.getSource().equals(checkOnTimeJCheckBox))
        {
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/checkOnTimeJCheckBox",checkOnTimeJCheckBox.isSelected());
        }
        else if(e.getSource().equals(keepZeroInScreenJCheckBox))
        {
            CCMConfigManager.setBoolean("ccm/component/oscilloscope/keepZeroInScreenJCheckBox",keepZeroInScreenJCheckBox.isSelected());
        }
        else if(e.getSource().equals(selectAllJCheckBox))
        {
            for(int i=0;i<dataValueJLabels.length;++i)
            {
                dataValueJCheckBox[i].setSelected(selectAllJCheckBox.isSelected());
                CCMConfigManager.setBoolean("ccm/component/oscilloscope/custom/"+i,dataValueJCheckBox[i].isSelected());
            }
        }
        else
        {
            boolean checked=true;
            for(int i=0;i<dataValueJLabels.length;++i)
            {
                checked&=dataValueJCheckBox[i].isSelected();
                CCMConfigManager.setBoolean("ccm/component/oscilloscope/custom/"+i,dataValueJCheckBox[i].isSelected());
            }
            selectAllJCheckBox.setSelected(checked);
        }
        needFresh=true;
    }

    public void dragMouseRepaint(MouseEvent e)
    {
    }

    public void enableAutoBiasAndResize(boolean b)
    {
        dragMouse.enableAutoBiasAndResize(b);
    }


    /**
     * 获取显示区域的起始下标
     *
     * @param dataManager 数据管理器
     * @return 显示区域的起始下标
     */
    protected int getStartI(OscilloscopeDataManager dataManager)
    {
        return Math.min(dataManager.getDataLength(),(int)biasXJSpinner.getValue())-1;
    }

    @Override
    public void setNeedFresh(boolean needFresh)
    {
        oscilloscopeGLJPanelNeedFresh=needFresh;
    }

    @Override
    public void setNeedFastFresh(boolean needFastFresh)
    {
        oscilloscopeGLJPanelNeedFastFresh=needFastFresh;
    }

    class DDDragMouse extends DragMouse
    {
        public DDDragMouse()
        {
            super();
        }

        public DDDragMouse(DragMouseListener dragMouseListener,JSpinner resizeYJSpinner,JCheckBox resizeYJCheckBox,JSpinner biasYJSpinner,JCheckBox biasYJCheckBox,JSpinner resizeXJSpinner,JCheckBox resizeXJCheckBox,JSpinner biasXJSpinner,JCheckBox biasXJCheckBox)
        {
            super(dragMouseListener,resizeYJSpinner,resizeYJCheckBox,biasYJSpinner,biasYJCheckBox,resizeXJSpinner,resizeXJCheckBox,biasXJSpinner,biasXJCheckBox);
        }

        protected int mouseDraggedBiasY(int bias,int v,int start,float resize)
        {
            return bias+v-start;
        }

        protected int mouseWheelMovedBiasY(float k,int bias,int v,float resizeOld)
        {
            return (int)(k*bias+v*(1-k));
        }

        protected int mouseDraggedBiasX(int bias,int v,int start,float resize)
        {
            return (int)(bias-(v-start)/((double)resize));
        }

        protected int mouseWheelMovedBiasX(float k,int bias,int v,float resizeOld)
        {
            double x1=getStartI(allDataManager);
            double x2=(x1-(glJPanel.getBounds().width-v)/((double)resizeOld));
            return (int)(x2+(x1-x2)/((double)k));
        }
    }

    protected class CursorJDialog extends JDialog
    {
        public CursorJDialog(JFrame owner)
        {
            super(owner,updateCurse());

            final int usableColumnN=Math.min(dataValueJLabels.length,allDataManager.getDataColumn());
            boolean[] columnInScreen=new boolean[usableColumnN];
            {
                for(int j=0;j<usableColumnN;++j)
                {
                    columnInScreen[j]=dataValueJCheckBox[j].isSelected();
                }
                for(int j=0;j<usableColumnN;++j)
                {
                    for(int i=curse[0];i<=curse[1]&&columnInScreen[j];++i)
                    {
                        columnInScreen[j]&=allDataManager.isColumnEnabled(i,j)&&dataValueJCheckBox[j].isSelected();
                    }
                }
            }
            // 创建一个模态对话框
            setAlwaysOnTop(true);
            setSize(800,400);
            setResizable(true);
            setLocationRelativeTo(owner);
            JPanel dialogJPanel=new JPanel();
            dialogJPanel.setLayout(new BorderLayout());
            setContentPane(dialogJPanel);
            JPanel buttonJPanel=new JPanel();
            buttonJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
            add(buttonJPanel,BorderLayout.NORTH);

            JButton exportJButton=new JButton("导出");
            exportJButton.setFont(CCMConfigManager.getFont("ccm/general/font"));
            exportJButton.setEnabled(curseI==2);
            buttonJPanel.add(exportJButton);
            exportJButton.addActionListener(e->
            {
                JFileChooser fileChooser=new JFileChooser();
                fileChooser.setFileFilter(new FileNameExtensionFilter("CSV","csv"));
                fileChooser.setCurrentDirectory(new File(CCMConfigManager.setStringIfAbsent("ccm/general/curseDir",".")));
                fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
                fileChooser.setMultiSelectionEnabled(false);
                int result=fileChooser.showOpenDialog(this);
                if(result==JFileChooser.APPROVE_OPTION)
                {
                    File file=fileChooser.getSelectedFile();
                    if(!file.getAbsolutePath().endsWith(".csv"))
                    {
                        file=new File(file+".csv");
                    }
                    CCMConfigManager.setString("ccm/general/curseDir",file.getAbsolutePath());
                    final String text=updateCurse();
                    if(curseI!=2)
                    {
                        JOptionPane.showMessageDialog(this,"游标不够","导出失败",JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    try
                    {
                        BufferedWriter writeText=new BufferedWriter(new FileWriter(file));
                        for(int j=0, cnt=0;j<usableColumnN;++j)
                        {
                            if(columnInScreen[j])
                            {
                                if(cnt!=0)
                                {
                                    writeText.write(",");
                                }
                                ++cnt;
                                final String columnName=allDataManager.getColumnName(curse[0],j);
                                writeText.write(columnName);
                            }
                        }
                        for(int i=curse[0];i<=curse[1];++i)
                        {
                            writeText.newLine();
                            for(int j=0, cnt=0;j<usableColumnN;++j)
                            {
                                if(columnInScreen[j])
                                {
                                    if(cnt!=0)
                                    {
                                        writeText.write(",");
                                    }
                                    ++cnt;
                                    writeText.write(String.format("%.6f",allDataManager.getY(i,j)));
                                }
                            }
                        }
                        writeText.flush();
                        writeText.close();
                        JOptionPane.showMessageDialog(this,"已将"+text+"保存到"+file.getAbsolutePath(),"导出成功",JOptionPane.INFORMATION_MESSAGE);
                    }catch(IOException ex)
                    {
                        JOptionPane.showMessageDialog(this,ex.getMessage(),"导出失败",JOptionPane.ERROR_MESSAGE);
                    }
                }
            });
            //计算均值
            {
                JPanel avgJPanel=new JPanel();
                add(avgJPanel);
                {
                    int cnt=1;
                    for(int j=0;j<usableColumnN;++j)
                    {
                        if(columnInScreen[j])
                        {
                            ++cnt;
                        }
                    }
                    avgJPanel.setLayout(new GridLayout(cnt,5));
                }
                avgJPanel.add(new JLabel("下标"));
                avgJPanel.add(new JLabel("均值"));
                avgJPanel.add(new JLabel("最大值"));
                avgJPanel.add(new JLabel("最小值"));
                avgJPanel.add(new JLabel("最大最小差"));
                MouseAdapter mouseAdapter=new MouseAdapter()
                {
                    @Override
                    public void mouseClicked(MouseEvent e)
                    {
                        //复制到剪切板
                        super.mouseClicked(e);
                        if(e.getClickCount()>=2&&e.getButton()==MouseEvent.BUTTON1)
                        {
                            String txt=((JLabel)e.getSource()).getText();
                            StringSelection data=new StringSelection(txt);
                            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(data,data);
                        }
                    }
                };

                for(int j=0;j<usableColumnN;++j)
                {
                    if(columnInScreen[j])
                    {
                        double sum=0;
                        float max=allDataManager.getY(curse[0],j);
                        float min=allDataManager.getY(curse[0],j);
                        for(int i=curse[0];i<=curse[1];++i)
                        {
                            sum+=allDataManager.getY(i,j);
                            max=Math.max(max,allDataManager.getY(i,j));
                            min=Math.min(min,allDataManager.getY(i,j));
                        }
                        avgJPanel.add(new JLabel(allDataManager.getColumnName(curse[0],j)));
                        final JLabel[] jls=new JLabel[4];
                        avgJPanel.add(jls[0]=new JLabel(String.format("%.6f",sum/(curse[1]-curse[0]+1))));
                        avgJPanel.add(jls[1]=new JLabel(String.format("%.6f",max)));
                        avgJPanel.add(jls[2]=new JLabel(String.format("%.6f",min)));
                        avgJPanel.add(jls[3]=new JLabel(String.format("%.6f",max-min)));
                        for(JLabel jl: jls)
                        {
                            jl.addMouseListener(mouseAdapter);
                        }
                    }
                }
            }
            setVisible(true);
        }
    }

    /**
     * 绘制波形的面板
     */
    protected class OscilloscopeGLJPanel extends GLJPanel implements GLEventListener, KeyListener
    {
        private final TextRenderer axisTextRenderer=new TextRenderer(axisFont);
        private final TextRenderer fpsTextRenderer=new TextRenderer(fpsFont);
        private final TextRenderer mouseTextRenderer=new TextRenderer(mouseFont);
        private final TextRenderer mouseNameTextRenderer=new TextRenderer(mouseNameFont);
        /**
         * 上一次显示的时间用于计算帧率和锁帧
         */
        protected long lastMillSecond;
        private int lastDataLength;

        OscilloscopeGLJPanel()
        {
            super();
            setBackground(backgroundColor);
            addMouseListener(dragMouse);
            addMouseMotionListener(dragMouse);
            addMouseWheelListener(dragMouse);
            addGLEventListener(this);
            addKeyListener(this);
            addMouseListener(new MouseListener()
            {
                @Override
                public void mouseClicked(MouseEvent e)
                {
                    OscilloscopeGLJPanel.super.requestFocus();
                }

                @Override
                public void mousePressed(MouseEvent e)
                {

                }

                @Override
                public void mouseReleased(MouseEvent e)
                {

                }

                @Override
                public void mouseEntered(MouseEvent e)
                {

                }

                @Override
                public void mouseExited(MouseEvent e)
                {

                }
            });

            curseI=0;
            updateCurse();
            Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new OscilloscopeGLJPanel.FreshThread(),0,10,TimeUnit.MILLISECONDS);
            needFresh=true;
            oscilloscopeGLJPanelNeedFresh=true;
            lastDataLength=-1;
            repaint();
        }

        @Override
        public void init(GLAutoDrawable glAutoDrawable)
        {
            glAutoDrawable.setAutoSwapBufferMode(false);
            axisTextRenderer.setColor(axisColor);
            fpsTextRenderer.setColor(fpsColor);
            mouseTextRenderer.setColor(mouseColor);
        }

        @Override
        public void dispose(GLAutoDrawable glAutoDrawable)
        {

        }

        private int getFontWidth(Font font,String str)
        {
            return (this).getFontMetrics(font).stringWidth(str);
        }

        private int getFontHeight(Font font)
        {
            return (this).getFontMetrics(font).getHeight();
        }

        @Override
        synchronized public void display(GLAutoDrawable glAutoDrawable)
        {
            if(!isDisplayable()||!oscilloscopeGLJPanelNeedFresh)
            {
                return;
            }
            final int width=getBounds().width;
            final int height=getBounds().height;
            /*
             * 计算帧率并进行锁帧
             */
            final long startMs=System.currentTimeMillis();
            if(startMs-lastMillSecond<((oscilloscopeGLJPanelNeedFastFresh||dragMouse.isNeedFresh())?10:50)*0.9f)
            {
                return;
            }
            final int fps=(int)(1000/(startMs-lastMillSecond+0.0001));
            lastMillSecond=startMs;
            if(allDataManager==null)
            {
                /*清空画板*/
                final GL2 gl=glAutoDrawable.getGL().getGL2();
                clean(gl);
                gl.glColor4f(fpsColor.getRed()/255.0f,fpsColor.getGreen()/255.0f,fpsColor.getBlue()/255.0f,fpsColor.getAlpha()/255.0f);
                drawString(fpsTextRenderer,width,height,0,getFontHeight(fpsFont),"no data manager");
                gl.glFlush();
                glAutoDrawable.swapBuffers();
                return;
            }
            /*
             * 复制及创建必要的变量
             */
            final float resizeX=((Number)(resizeXJSpinner.getValue())).floatValue();
            final int startI=getStartI(allDataManager);
            final int endI=Math.max(0,(int)(startI-width/(double)resizeX));
            if(!(needFresh||dragMouse.isNeedFresh()||allDataManager.getDataLength()!=lastDataLength))
            {
                return;
            }
            lastDataLength=allDataManager.getDataLength();
            allDataManager.setFastUpdate(biasXJCheckBox.isSelected());
            needFresh=false;
            dragMouse.setNeedFresh(false);
            final int usableColumnN=Math.min(dataValueJLabels.length,allDataManager.getDataColumn());
            for(int i=0;i<dataValueJLabels.length;++i)
            {
                dataJPanel[i].setVisible(i<usableColumnN);
                dataResizeJSpinners[i].setVisible(i<usableColumnN);
                dataValueJCheckBox[i].setVisible(i<usableColumnN);
                dataValueJLabels[i].setVisible(i<usableColumnN);
            }
            final MouseData nowMouseData=dragMouse.getMouseData();
            final float[] dataResizeY=new float[usableColumnN];
            for(int j=0;j<usableColumnN;++j)
            {
                dataResizeY[j]=((Number)(dataResizeJSpinners[j].getValue())).floatValue();
            }
            final int iIncPer=largeStepJCheckBox.isSelected()?((int)Math.ceil(1.0/(double)resizeX)):1;
            /*
             *计算哪些通道在屏幕上有数据
             */
            boolean[] columnInScreen=new boolean[usableColumnN];
            for(int j=0;j<usableColumnN;++j)
            {
                if(dataValueJCheckBox[j].isSelected())
                {
                    for(int i=endI;i<=startI;i+=iIncPer)
                    {
                        columnInScreen[j]|=allDataManager.isColumnEnabled(i,j);
                    }
                }
                else
                {
                    columnInScreen[j]=false;
                }
            }
            /*
             * 有数据并且需要Y轴自动缩放或者偏移就进行自动偏移的计算
             * 从前往后遍历所有可以显示的数据获得最大和最小的Y值
             */
            if(resizeYJCheckBox.isSelected()||resizeYJCheckBox.isSelected())
            {
                boolean set=keepZeroInScreenJCheckBox.isSelected();
                float yMax=0;
                float yMin=0;
                for(int j=0;j<usableColumnN;++j)
                {
                    for(int i=endI;i<=startI;i+=iIncPer)
                    {
                        if(allDataManager.isColumnEnabled(i,j)&&columnInScreen[j])
                        {
                            float tmp=handleNan(allDataManager.getY(i,j))*dataResizeY[j];
                            if(set)
                            {
                                yMax=Math.max(yMax,tmp);
                                yMin=Math.min(yMin,tmp);
                            }
                            else
                            {
                                yMax=yMin=tmp;
                                set=true;
                            }
                        }
                    }
                }
                /*
                 * 特殊处理两者相等的情况,放置出现除0异常
                 */
                if(yMax==yMin)
                {
                    ++yMax;
                    --yMin;
                }
                /*
                 * 计算偏移量并进行低通滤波
                 */
                if(resizeYJCheckBox.isSelected())
                {
                    float yB=(0.8f*height/(yMax-yMin))*0.5f+((Number)(resizeYJSpinner.getValue())).floatValue()*(1-0.5f);
                    Utils.setJSpinnerValue(resizeYJSpinner,yB);
                }
                if(biasYJCheckBox.isSelected())
                {
                    int yA=(int)(height*0.9+yMin*((Number)(resizeYJSpinner.getValue())).floatValue());
                    Utils.setJSpinnerValue(biasYJSpinner,yA);
                }
            }
            /*
             * 获取Y轴偏移量和缩放比例
             */
            final int yA=(int)biasYJSpinner.getValue();
            final float yB=((Number)(resizeYJSpinner.getValue())).floatValue();
            /*清空画板*/
            final GL2 gl=glAutoDrawable.getGL().getGL2();
            clean(gl);
            /*绘制坐标轴*/
            gl.glColor4f(axisColor.getRed()/255.0f,axisColor.getGreen()/255.0f,axisColor.getBlue()/255.0f,axisColor.getAlpha()/255.0f);
            gl.glLineWidth(axisWidth);
            /*
             * 绘制竖着的坐标轴
             */
            for(int n=Integer.highestOneBit(Math.max(1,(int)(200/((double)resizeX)))), x=(int)(width-(startI*((double)resizeX))%(n*((double)resizeX))), i=(int)(startI-(width-x)/((double)resizeX));x>=0&&i>=0;x-=(int)(n*((double)resizeX)),i=(int)(startI-(width-x)/((double)resizeX)))
            {
                drawLine(gl,width,height,x,0,x,height);
                drawString(axisTextRenderer,width,height,x,yA,String.format("%d",i));
            }
            /*绘制横向的坐标轴*/
            for(int k=0;yA-k>=0||yA+k<=height;k+=100)
            {
                drawLine(gl,width,height,0,yA-k,width,yA-k);
                drawLine(gl,width,height,0,yA+k,width,yA+k);
                // bufG.drawString(String.format("%.2f",(yA-(yA-k))*yB),0,yA-k);
                // bufG.drawString(String.format("%.2f",(yA-(yA+k))*yB),0,yA+k);
            }
            /*绘制曲线*/
            if(allDataManager.getDataLength()>0&&startI>=0)
            {
                if(checkOnChan0JCheckBox.isSelected()&&columnInScreen[0])
                {
                    gl.glColor4f(1,0,0,0.25f);
                    gl.glLineWidth(lineWidth);
                    for(int i=Math.max(1,endI);i<=startI;i+=1)
                    {
                        final float x1=((width-(startI-i)*resizeX));
                        final float y=handleNan(allDataManager.getY(i,0));
                        final float py=handleNan(allDataManager.getY(i-1,0));
                        if(((((int)(py+128))+1)&0XFF)!=((int)(y+128)))
                        {
                            drawLine(gl,width,height,x1,0,x1,height);
                        }
                    }
                }
                if(checkOnTimeJCheckBox.isSelected())
                {
                    gl.glColor4f(0,1,0,0.25f);
                    gl.glLineWidth(lineWidth);
                    for(int i=Math.max(2,endI);i<=startI;i+=1)
                    {
                        final float x1=((width-(startI-i)*resizeX));
                        final long dt1=allDataManager.getTimeUs(i-1)-allDataManager.getTimeUs(i-2);
                        final long dt2=allDataManager.getTimeUs(i)-allDataManager.getTimeUs(i-1);
                        if((1.0f*(dt2-dt1)/dt1)>0.1)
                        {
                            drawLine(gl,width,height,x1,0,x1,height);
                        }
                    }
                }
                for(int j=0;j<usableColumnN;++j)
                {
                    if(columnInScreen[j])
                    {
                        gl.glColor4f(colors[j%colors.length].getRed()/255.0f,colors[j%colors.length].getGreen()/255.0f,colors[j%colors.length].getBlue()/255.0f,colors[j%colors.length].getAlpha()/255.0f);
                        gl.glLineWidth(lineWidth);
                        boolean inLine=false;
                        final float ybRe=yB*dataResizeY[j];
                        for(int i=endI;i<=startI;i+=iIncPer)
                        {
                            if(breakLineJCheckBox.isSelected()&&(!allDataManager.isContinuous(i))&&inLine)
                            {
                                gl.glEnd();
                                inLine=false;
                            }
                            if(allDataManager.isColumnEnabled(i,j))
                            {
                                final float x1=((width-(startI-i)*resizeX));
                                final float y1=yA-ybRe*handleNan(allDataManager.getY(i,j));
                                if(!inLine)
                                {
                                    inLine=true;
                                    gl.glBegin(GL2.GL_LINE_STRIP);
                                }
                                drawPoint(gl,width,height,x1,y1);
                            }
                            else
                            {
                                if(inLine)
                                {
                                    gl.glEnd();
                                    inLine=false;
                                }
                            }
                        }
                        if(inLine)
                        {
                            gl.glEnd();
                        }
                    }
                }
            }
            //更新游标
            updateCurse();
            //绘制游标
            {
                gl.glLineWidth(curseWidth);
                gl.glColor4f(curseColor.getRed()/255.0f,curseColor.getGreen()/255.0f,curseColor.getBlue()/255.0f,curseColor.getAlpha()/255.0f);
                int[] x=new int[2];
                for(int j=0;j<curseI;++j)
                {
                    x[j]=(int)Math.min(width-curseWidth/2,Math.max(curseWidth/2,((width-(startI-curse[j])*resizeX))));
                    drawLine(gl,width,height,x[j],0,x[j],height);
                }
                if(curseI==2)
                {
                    drawLine(gl,width,height,x[0],curseWidth/2,x[1],curseWidth/2);
                    drawLine(gl,width,height,x[0],height-curseWidth/2,x[1],height-curseWidth/2);
                }
            }
            /*
             * 绘制鼠标轴
             */
            gl.glLineWidth(mouseWidth);
            gl.glColor4f(mouseColor.getRed()/255.0f,mouseColor.getGreen()/255.0f,mouseColor.getBlue()/255.0f,mouseColor.getAlpha()/255.0f);
            final Point nowMousePoint=nowMouseData.getPoint();
            if(nowMousePoint!=null)

            {
                drawLine(gl,width,height,nowMousePoint.x,0,nowMousePoint.x,height);
                drawLine(gl,width,height,0,nowMousePoint.y,width,nowMousePoint.y);
            }
            /*
             * 绘制鼠标上的数据点
             */
            if(nowMousePoint!=null&&(allDataManager.getDataLength()>0&&startI>=0))
            {
                final int mouthI=(int)((startI-(width-nowMousePoint.x)/resizeX));
                if(mouthI>=0)
                {
                    //当前所在的数据行对应数据列的值
                    final float[] y=new float[usableColumnN];
                    //显示值的坐标
                    final int[] y1=new int[usableColumnN];
                    {
                        int cnt=0;                                              //有多少个可用的值
                        final int[] order=new int[usableColumnN]; //顺序
                        //取到当前的值
                        for(int j=0;j<usableColumnN;++j)
                        {
                            if(columnInScreen[j]&&allDataManager.isColumnEnabled(mouthI,j))
                            {
                                y[j]=handleNan(allDataManager.getY(mouthI,j))*dataResizeY[j];
                                order[cnt]=j;
                                //冒泡排序
                                for(int k=cnt-1;k>=0;--k)
                                {
                                    if(y[order[k]]>y[order[k+1]])
                                    {
                                        int tmp=order[k+1];
                                        order[k+1]=order[k];
                                        order[k]=tmp;
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                ++cnt;
                            }
                        }
                        for(int k=0, top=getFontHeight(mouseFont)+getFontHeight(mouseNameFont)+8;k<cnt;++k,top+=getFontHeight(mouseFont)+getFontHeight(mouseNameFont)+8)
                        {
                            final int topJ=order[cnt-1-k];
                            top=Math.max(top,Math.min((int)(yA-yB*y[topJ]),height));
                            y1[topJ]=top;
                        }
                        for(int k=0, bottom=height;k<cnt;++k,bottom-=getFontHeight(mouseFont)+getFontHeight(mouseNameFont)+8)
                        {
                            final int bottomJ=order[k];
                            bottom=Math.min(bottom,Math.max((int)(yA-yB*y[bottomJ]),y1[bottomJ]));
                            y1[bottomJ]=bottom;
                        }
                    }
                    /*
                     * 把数值画到画板上
                     */
                    boolean swapFlag=false;
                    int timeStartX=nowMousePoint.x;
                    for(int j=0;j<usableColumnN;++j)
                    {
                        if(columnInScreen[j])
                        {
                            final String text=String.format("%-10.6f",allDataManager.getY(mouthI,j));
                            final String columnName=allDataManager.getColumnName(mouthI,j);
                            final int fontWidth=Math.max(getFontWidth(mouseNameFont,columnName),getFontWidth(mouseFont,text));
                            if(nowMousePoint.x+fontWidth>width)
                            {
                                swapFlag=true;
                            }
                        }
                    }
                    for(int j=0;j<usableColumnN;++j)
                    {
                        setJCheckBoxText(dataValueJCheckBox[j],allDataManager.getColumnName(customNameJCheckBox.isSelected()?mouthI:-1,j));
                    }
                    for(int j=0;j<usableColumnN;++j)
                    {
                        if(columnInScreen[j])
                        {
                            final String text=String.format("%-10.6f",allDataManager.getY(mouthI,j));
                            final String columnName=allDataManager.getColumnName(mouthI,j);
                            final int fontWidth=Math.max(getFontWidth(mouseNameFont,columnName),getFontWidth(mouseFont,text));
                            final int fontHeight=getFontHeight(mouseFont)+getFontHeight(mouseNameFont);
                            final int xx1=nowMousePoint.x-(swapFlag?fontWidth:0);
                            final int yy1=y1[j]-fontHeight;
                            final int xx2=xx1+fontWidth;
                            final int yy2=yy1+fontHeight;
                            final int timeY1=nowMousePoint.y-2*getFontHeight(mouseFont)-4, timeY2=nowMousePoint.y+4;
                            if((timeY1<yy1&&yy1<timeY2)||(timeY1<yy2&&yy2<timeY2)||(timeY1<yy1&&yy2<timeY2)||(yy1<timeY1&&timeY2<yy2))
                            {
                                timeStartX=Math.min(timeStartX,xx1);
                            }
                            gl.glColor4f(mouseBackgroundColor.getRed()/255.0f,mouseBackgroundColor.getGreen()/255.0f,mouseBackgroundColor.getBlue()/255.0f,mouseBackgroundColor.getAlpha()/255.0f);
                            gl.glBegin(GL2.GL_QUADS);
                            drawPoint(gl,width,height,xx1,yy1);
                            drawPoint(gl,width,height,xx1,yy2);
                            drawPoint(gl,width,height,xx2,yy2);
                            drawPoint(gl,width,height,xx2,yy1);
                            gl.glEnd();
                            mouseTextRenderer.setColor(colors[j%colors.length]);
                            drawString(mouseTextRenderer,width,height,xx1+4,y1[j]-4,text);
                            mouseNameTextRenderer.setColor(colors[j%colors.length]);
                            drawString(mouseNameTextRenderer,width,height,xx1+4,(int)(y1[j]-getFontHeight(mouseFont)),columnName);
                            dataValueJLabels[j].setText(text);
                            dataValueJLabels[j].setToolTipText(text);
                        }
                    }
                    //画下标
                    {
                        mouseTextRenderer.setColor(mouseColor);
                        final String text=String.format("%d",mouthI);
                        drawString(mouseTextRenderer,width,height,timeStartX-getFontWidth(mouseFont,text),nowMousePoint.y-getFontHeight(mouseFont)-4,text);
                    }
                    //画时间戳
                    {
                        mouseTextRenderer.setColor(mouseColor);
                        final String text=timeFormatter(allDataManager.getTimeUs(mouthI));
                        drawString(mouseTextRenderer,width,height,timeStartX-getFontWidth(mouseFont,text),nowMousePoint.y-4,text);
                    }
                }
                else
                {
                    for(int j=0;j<usableColumnN;++j)
                    {
                        setJCheckBoxText(dataValueJCheckBox[j],allDataManager.getColumnName(customNameJCheckBox.isSelected()?(startI):-1,j));
                        dataValueJLabels[j].setText("");
                    }
                }
            }
            else
            {
                for(int j=0;j<usableColumnN;++j)
                {
                    setJCheckBoxText(dataValueJCheckBox[j],allDataManager.getColumnName(customNameJCheckBox.isSelected()?(startI):-1,j));
                    dataValueJLabels[j].setText("");
                }
            }
            /*
             * 显示帧率
             */
            gl.glColor4f(fpsColor.getRed()/255.0f,fpsColor.getGreen()/255.0f,fpsColor.getBlue()/255.0f,fpsColor.getAlpha()/255.0f);

            {
                String text=String.format("%d,%d,%d",startI-endI+1,allDataManager.getDataLength(),startI);
                drawString(fpsTextRenderer,width,height,0,getFontHeight(fpsFont)*2,text);
            }

            {
                long ms=System.currentTimeMillis();
                String text=String.format("fps:%d,cps:%d(%dms)",fps,1000/(ms-startMs+1),ms-startMs);
                drawString(fpsTextRenderer,width,height,0,getFontHeight(fpsFont),text);
            }

            gl.glFlush();
            glAutoDrawable.swapBuffers();
        }

        @Override
        public void reshape(GLAutoDrawable glAutoDrawable,int i,int i1,int i2,int i3)
        {

        }

        private void drawString(final TextRenderer textRenderer,int width,int height,int x,int y,final String string)
        {
            textRenderer.beginRendering(width,height);
            textRenderer.draw(string,x,height-y);
            textRenderer.endRendering();
        }

        private float handleNan(float x)
        {
            return Float.isFinite(x)?x:0;
        }

        private void drawLine(final GL2 gl,int width,int height,float x1,float y1,float x2,float y2)
        {
            gl.glBegin(GL2.GL_LINES);
            drawPoint(gl,width,height,x1,y1);
            drawPoint(gl,width,height,x2,y2);
            gl.glEnd();
        }

        private void clean(final GL2 gl)
        {
            gl.glClearColor(backgroundColor.getRed()/255.0f,backgroundColor.getGreen()/255.0f,backgroundColor.getBlue()/255.0f,backgroundColor.getAlpha()/255.0f);
            gl.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
            gl.glEnable(GL_POINT_SMOOTH);
            gl.glEnable(GL_LINE_SMOOTH);
            gl.glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
            gl.glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
            gl.glEnable(GL_BLEND);
            gl.glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
        }

        private void setJCheckBoxText(JCheckBox checkBox,String str)
        {
            checkBox.setText(str.substring(Math.max(0,str.length()-25)));
            checkBox.setToolTipText(str);
        }

        private void drawPoint(final GL2 gl,int width,int height,float x,float y)
        {
            gl.glVertex2f(-1.0f+2.0f*x/width,1.0f-2.0f*y/height);
        }

        @Override
        public void keyTyped(KeyEvent e)
        {

        }

        @Override
        public void keyPressed(KeyEvent e)
        {

        }

        CursorJDialog cursorJDialog=null;

        @Override
        public void keyReleased(KeyEvent e)
        {
            final int width=getBounds().width;
            final MouseData nowMouseData=dragMouse.getMouseData();
            final double resizeX=((Number)(resizeXJSpinner.getValue())).floatValue();
            final int startI=getStartI(allDataManager);
            if(nowMouseData==null||nowMouseData.getPoint()==null)
            {
                return;
            }
            final int mouthI=(int)((startI-(width-nowMouseData.getPoint().getX())/resizeX));
            if(e.getKeyCode()==KeyEvent.VK_Q)
            {
                curseI=(curseI+1)%3;
                if(curseI>=1)
                {
                    curse[curseI-1]=mouthI;
                }
            }
            else if(e.getKeyCode()==KeyEvent.VK_E)
            {
                curseI=0;
            }
            else if(e.getKeyCode()==KeyEvent.VK_R||e.getKeyCode()==KeyEvent.VK_T)
            {
                biasXJCheckBox.setSelected(false);
                if(curseI==0)
                {
                    curse[0]=0;
                    curse[1]=allDataManager.getDataLength()-1;
                    curseI=2;
                }
                if(curseI>=2)
                {
                    biasXJSpinner.setValue((curse[1]+curse[0])/2+(int)(width/resizeX/2));
                }
                else if(curseI>=1)
                {
                    biasXJSpinner.setValue(curse[0]+(int)(width/resizeX/2));
                }
                if(e.getKeyCode()==KeyEvent.VK_T&&curseI>=2)
                {
                    Point p=null;
                    if(cursorJDialog!=null)
                    {
                        p=cursorJDialog.getLocation();
                        cursorJDialog.dispose();
                    }
                    cursorJDialog=new CursorJDialog(null);
                    if(p!=null)
                    {
                        cursorJDialog.setLocation(p);
                    }
                }
            }
            updateCurse();
        }

        /**
         * 刷新面板用的线程类
         */
        protected class FreshThread implements Runnable
        {
            public void run()
            {
                updateBiasXJSpinner();
                if(!isDisplayable()||!oscilloscopeGLJPanelNeedFresh)
                {
                    return;
                }
                if(!(needFresh||dragMouse.isNeedFresh()||allDataManager.getDataLength()!=lastDataLength))
                {
                    return;
                }
                glJPanel.repaint();
            }
        }
    }
}
